
/**
 * Write a description of class AdvancedFilters here.
 * 
 * @author (your name) 
 * @version (a version number or a date)
 */
public class AdvancedFilters
{
    private Image target;
    public AdvancedFilters(Image i){
        target = i;
    }
    public Image mirror(){
        int maxX = target.getWidth() - 1, maxY = target.getHeight() - 1;
        Image output = new Image(target.getWidth(), target.getHeight(), "mirrored!");
        
        for(int x = 0 ; x < target.getWidth() ; x ++){
            for(int y = 0 ; y < target.getHeight() ; y ++){
                Pixel pt = target.getPixel(x, y);
                Pixel po = output.getPixel(maxX - x, y);
                
                po.setValue(pt.getValue());                
            }
        }
        output.pixelsUpdated();
        return output;
    }
    public Image flip(){
        int maxX = target.getWidth() - 1, maxY = target.getHeight() - 1;
        Image output = new Image(target.getWidth(), target.getHeight(), "flipped!");
        
        for(int x = 0 ; x < target.getWidth() ; x ++){
            for(int y = 0 ; y < target.getHeight() ; y ++){
                Pixel pt = target.getPixel(x, y);
                Pixel po = output.getPixel(x, maxY -  y);
                
                po.setValue(pt.getValue());                
            }
        }
        output.pixelsUpdated();
        return output;
    }
    
    public Image rotate(){
        int maxX = target.getWidth() - 1, maxY = target.getHeight() - 1;
        Image output = new Image(target.getHeight(), target.getWidth(), "rotated!");
        
        for(int x = 0 ; x < target.getWidth() ; x ++){
            for(int y = 0 ; y < target.getHeight() ; y ++){
                Pixel pt = target.getPixel(x, y);
                Pixel po = output.getPixel(y, maxX -  x);
                
                po.setValue(pt.getValue());                
            }
        }
        output.pixelsUpdated();
        return output;
    }
    public Image blur(){
        int maxX = target.getWidth() - 1, maxY = target.getHeight() - 1;
        Image output = new Image(target.getWidth(), target.getHeight(), "blurred!");
        
        for(int x = 0 ; x < target.getWidth() ; x ++){
            for(int y = 0 ; y < target.getHeight() ; y ++){
                Pixel po = output.getPixel(x, y);
                po.setValue(average(x, y));                
            }
        }
        output.pixelsUpdated();
        return output;
    }
    
    public Image gaussianBlur(double sigma, int size){
        GaussianTable T = new GaussianTable(sigma, size);
        int maxX = target.getWidth() - 1, maxY = target.getHeight() - 1;
        Image output = new Image(target.getWidth(), target.getHeight(), "gaussian blurred!");
        
        for(int x = 0 ; x < target.getWidth() ; x ++){
            for(int y = 0 ; y < target.getHeight() ; y ++){
                Pixel po = output.getPixel(x, y);
                po.setValue(gaussianAverage(x, y, T));                
            }
        }
        output.pixelsUpdated();
        return output;
    }
    public Image scale(double factor){
        Image output = new Image((int)(target.getWidth() * factor), (int)(target.getHeight() * factor), "scaled!");
        double inverseFactor = 1.0 / factor;
        
        for(int x = 0 ; x < output.getWidth() ; x ++){
            for(int y = 0 ; y < output.getHeight() ; y ++){
                int tx = (int)(x * inverseFactor);
                int ty = (int)(y * inverseFactor);
                if(tx == output.getWidth()){
                    tx -= 1;
                }
                if(ty == output.getHeight()){
                    ty -= 1;
                }
                
                Pixel pt = target.getPixel(tx, ty);                
                Pixel po = output.getPixel(x, y);
                
                po.setValue(pt.getValue());
            }
        }
        output.pixelsUpdated();
        return output;
    }
    
    private int average(int x, int y){
        int sum = 0, count = 0;
        for(Pixel p: target.getNeighbours(x, y)){
            sum += p.getValue();
            count++;
        }
        if(count == 0)
            return 0;
        return (sum / count);
    }
    private boolean inside(int x, int y){
        if(x >= 0 && x < target.getWidth())
            if(y >= 0 && y < target.getHeight())
                return true;
        return false;
    }
    private int gaussianAverage(int x, int y, GaussianTable T){
        Pixel p;
        double sum = 0;
        double extra = 0.0;
        for(int i = - T.getOffset() ; i <= T.getOffset() ; i ++){
            for(int n = -T.getOffset() ; n <= T.getOffset() ; n ++){
                if(inside(x+i, y+n)){
                    p = target.getPixel(x + i, y + n);
                    sum += T.getWeight(i, n) * (double)(p.getValue());
                } else {
                    extra += T.getWeight(i, n) * 127.0;
                }
            }
        }
        return (int)(sum + extra);
    }
}
